// Three pieces need to be configured to use Passport for authentication:
//
// 1. Authentication strategies
// 2. Application middleware
// 3. Sessions (optional)
//
// After authenticated, the passport will create:
//      req.user, req.isAuthenticated(), etc..

/**

Step 1: declare authenticate strategies.
passport.use( 'strategy_name', new XxxxStrategy(authenticate_function(xxx, yyy, next){
   .....
   .....
   depends on your strategy you use, the xxx, yyy might be different
   but you need to use xxx, yyy to see if they match whats in your database.
   if it does, then find that entity: user
   
   next(null, user); ----> this will invoke passport.serializeUser(with the user passed in by 'next')
});

Step2: serialize the id of the entity user
hence, take the entity 'user', serialize part of it, usually 'id', and put it into session variable
passport.serializeUser(function(user, done) {
  user ===> userJson, i.e. {uid: user._id, test: 'test test'}
  
  done(null,  userJson );   // serialize obj into session cookie
}); 

Step3: deserialize to get entity user
here, if session contain serilized user parts, usually 'id',
deserialize it, and see if we can retrieve the real entity 'user' from database
passport.deserializeUser(function(userJson, done) {
  userJson ==> user
  done(null, user);
});

Step4: pass the middleware
now, as middleware that you need it to exame authentication:
app.route('/ttt/uuu')
   .get(passport.authenticate('strategy_name'), function(req, res) {
     // alsdfasl;dfkjasld
   });


*/


// global function
function CapFirstLetter(string) {
   return string.charAt(0).toUpperCase() + string.slice(1);
}


var LocalStrategy = require('passport-local').Strategy;
var GoogleStrategy = require('passport-google').Strategy;
var BasicStrategy = require('passport-http').BasicStrategy;   // simple and basic
var OauthStrategy = require('passport-http-oauth').Strategy;
var Oauth2Strategy = require('passport-http-bearer').BearerStrategy;

    MAX_LOGIN_ATTEMPTS = 5;
var LOCKED_TIME = 1000 * 60 * 60 * 2;  // two hours

    
module.exports = function(passport) {

   /*@Override*/ 
   passport.serializeUser(function(user, done) {
      done(null,  {uid: user._id, test: 'test test'} );   // serialize obj into session cookie
   });

   /*@override*/ 
   passport.deserializeUser(function(cookie_obj, done) {
      var uid = cookie_obj.uid;      
      // populate 'club' because of this: user.'club'
      Domain.User.findById(uid).populate('club').populate('joinedclubs').exec(function(err, user) { 
         done(err, user);
      });
   });

   /*Signup function*/
   var signupUser = function(theUser, req, nextCallback) {
      var regex    = new RegExp(["^", theUser.email, "$"].join(""), "i");
      Domain.User.findOne({'local.email': regex }, function(err, user) {
         if (err) return nextCallback(err);
         if (user) return nextCallback(null, false, req.flash('signupMessage', 'That email is already taken.'));

         // if there is no user with that email
         var newUser            = new Domain.User();
         newUser.local.email    = theUser.email;
         newUser.local.password = newUser.generateHash(theUser.password);
         newUser.firstname      = theUser.firstname;
         newUser.lastname       = theUser.lastname;
         newUser.login_attempts = 1;
         newUser.role           = 'member';

         newUser.save(function(err) {
            if (err) throw err;
            return nextCallback(null, newUser);
         });
      });
   };

   /*login function*/
   var loginUser = function(theUser, req, nextCallback) {
      var regex = new RegExp(["^", theUser.email, "$"].join(""), "i");
      Domain.User.findOne({'local.email': regex}, function(err, user) {
         if (err) return nextCallback(err, null);

         // User Not Found
         if (!user) return nextCallback(null, false, req.flash('loginMessage', 'The email or password you entered is incorrect.'));

         // User Found With Locked Account
         if (Date.now() < user.locked_until) {
            return nextCallback(null, false, req.flash('loginMessage', 'This account has been locked until ' + user.locked_until));

         // User Found With Incorrect Password
         } else if (!user.validPassword(theUser.password)) {  

            // Login Attempts < MAX_LOGIN_ATTEMPTS
            if (user.login_attempts < MAX_LOGIN_ATTEMPTS) {
               Domain.User.update({_id: user._id}, {$inc: {login_attempts: 1}}, function(err, num) {
                  if(err) throw err;
                  return nextCallback(null, false, req.flash('loginMessage', 
                                                             'This account will be locked after ' + 
                                                             (MAX_LOGIN_ATTEMPTS - user.login_attempts) + 
                                                             ' more failed logins.' ));
               });

            // Login Attempts >= MAX_LOGIN_ATTEMPTS
            } else {
               Domain.User.update({_id: user._id}, {login_attempts: 1, locked_until: Date.now() + LOCKED_TIME}, function(err, num) {
                  if(err) throw err;
                  return nextCallback(null, false, req.flash('loginMessage', 'This account has been locked.'));
               });
            }

         // User Found with Correct Password
         } else {
            Domain.User.update({_id: user._id}, {login_attempts : 1}, function(err, num) {
               if (err) throw err;
               return nextCallback(null, user);   // to access user in router: req.user, then it will be passed to 'user' in view
            });
         }
      });
   };

   var apiLoginUser = function(theUser, req, nextCallback) {
      var regex = new RegExp(["^", theUser.email, "$"].join(""), "i");
      Domain.User.findOne({'local.email': regex}, function(err, user) {
         if (err || !user) { return nextCallback(err, null);
         } else if (Date.now() < user.locked_until) {
            return nextCallback(null, null);
         } else if (!user.validPassword(theUser.password)) {  
            if (user.login_attempts < MAX_LOGIN_ATTEMPTS) {
               Domain.User.update({_id: user._id}, {$inc: {login_attempts: 1}}, function(err, num) {
                  return nextCallback(null, null);
               });
            } else {
               Domain.User.update({_id: user._id}, {login_attempts: 1, locked_until: Date.now() + LOCKED_TIME}, function(err, num) {
                   return nextCallback(null, null);
               });
            }
         } else {
            Domain.User.update({_id: user._id}, {login_attempts : 1}, function(err, num) {
               if (err) return nextCallback(null, null);
               else     return nextCallback(null, user); 
            });
         }
      });
   }



   //========================== Strategies ===================================================
   passport.use('my-local-signup', new LocalStrategy({
      // by default, local strategy uses username and password, we will override with email
      usernameField     : 'email',
      passwordField     : 'password',
      passReqToCallback : true // allows us to pass back the entire request to the callback
   }, function(req, email, password, done) {
      //var regex    = new RegExp(["^", email, "$"].join(""), "i");
      var theUser  = {'email': email, 
                      'password': password,
                      'firstname': CapFirstLetter(req.body.firstname),
                      'lastname': CapFirstLetter(req.body.lastname)};

      return signupUser(theUser, req, done);
   }));

   passport.use('my-local-login', new LocalStrategy({
      usernameField     : 'email',
      passwordField     : 'password',
      passReqToCallback : true 
   }, function(req, email, password, done) { // callback with email and password from our form
      var theUser  = {'email': email, 
                      'password': password};
      return loginUser(theUser, req, done);
   }));

   passport.use('api-local-login2', new LocalStrategy({
      usernameField     : 'email',
      passwordField     : 'password',
      passReqToCallback : true 
   }, function(req, email, password, done) { // callback with email and password from our form
      var theUser  = {'email': email, 
                      'password': password};
      return apiLoginUser(theUser, req, done);
   }));

   passport.use('google', new GoogleStrategy({
      returnURL: 'http://127.0.0.1',
      realm: 'http://127.0.0.1'
   },function (identifier, profile, done) {
      console.log(identifier);
      console.log(profile);
      var newUser            = new Domain.User();
      newUser._id = profile.emails[0];
      newUser.local.email    = profile.emails[0];
      newUser.firstname      = profile.name.givenName;
      newUser.lastname       = profile.name.familyName;
      newUser.login_attempts = 1;
      newUser.role           = 'member';
      return done(null, newUser);
   })); 


   passport.use('http-basic', new BasicStrategy({
       // http-basic - the name for the authentication
       // when pass as middleware, we can do this:
       // app.route('/xxx/xxx', passport.authenticate('http-basic'), function(req, res) { ... });
       usernameField     : 'email',
       passwordField     : 'password',
       passReqToCallback : true 
   }, function(req, email, password, done) {
      var regex = new RegExp(["^", email, "$"].join(""), "i");
   }));

};



